package top.lingkang.finalgateway.core;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.lingkang.finalgateway.config.RouterConfig;
import top.lingkang.finalgateway.config.RouterType;
import top.lingkang.finalgateway.core.http.HttpHandler;
import top.lingkang.finalgateway.utils.HttpUtils;
import top.lingkang.finalgateway.core.ws.FinalWebSocketServerProtocolHandler;
import top.lingkang.finalgateway.core.ws.WebSocketInitializer;

/**
 * 调度器
 *
 * @author lingkang
 * @create by 2024/5/10 15:09
 */
@Slf4j
public class DispatcherHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private final RouterConfig config;
    private static final Logger access = LoggerFactory.getLogger("access");

    public DispatcherHandler(RouterConfig config) {
        this.config = config;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        String ip = HttpUtils.getIpv4Address(ctx.channel());
        if (!config.getIpListCheck().checkIp(ip)) {
            // 不允许访问的ip
            HttpUtils.return403Forbidden(ctx);
            ctx.fireChannelRead(request);
            access.warn("forbidden http ip={} method={} url={} User-Agent={}",
                    ip, request.method().name(), request.uri(), request.headers().get("User-Agent"));
            return;
        }
        // websocket处理
        if ("websocket".equals(request.headers().get("Upgrade"))) {
            // 访问日志
            access.info("ws ip={} method={} url={} User-Agent={}",
                    ip, request.method().name(), request.uri(), request.headers().get("User-Agent"));
            RequestHandler handler = config.getHandlerMap().get(request.uri());
            if (handler == null) {
                handler = config.convertWs(request.uri());
                config.getHandlerMap().put(request.uri(), handler);
            }

            if (handler.getType() == RouterType.PROXY_WEBSOCKET) {
                // 开始握手连接
                ctx.pipeline().addLast(new WebSocketServerCompressionHandler());
                ctx.pipeline().addLast(new FinalWebSocketServerProtocolHandler(
                        request.uri(), //路径
                        null,
                        true,
                        config.getMaxContentLength(),//最大处理数据内容
                        false,  //掩码加密
                        true //允许 websocketPath 路径匹配，否则走全匹配，例如 websocketPath=/ws request=/ws?user=zhangsan 将匹配不上，无法处理
                ));
                //websocket 处理
                ctx.pipeline().addLast(new WebSocketInitializer(request.headers(), handler, config));
                // 后续处理
                ctx.fireChannelRead(request.retain());
            } else {
                // log.warn("未找到websocket处理, 它将被直接关闭连接. ws={}", request.uri());
                HttpUtils.closeHttpWebsocket(ctx, "404");
            }
            return;
        }

        // http处理
        ctx.pipeline().addLast(new ChunkedWriteHandler());
        // http 处理
        ctx.pipeline().addLast(new HttpHandler(config, ip));
        // next 并保留请求
        ctx.fireChannelRead(request.retain());
    }
}
